﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;

namespace GBricks
{
  public static class Operations
  {
    #region Constants

    public static bool True<T>(T value) {
      return true;
    }

    public static Func<T, bool> True<T>() {
      return True;
    }

    public static bool False<T>(T value) {
      return false;
    }

    public static Func<T, bool> False<T>() {
      return False;
    }

    public static void Void<T>(T value) {
    }

    public static Action<T> Void<T>() {
      return Void;
    }

    #endregion Constants

    #region Not<T>(…, Func<T, bool> predicate)

    public static bool Not<T>(T value, Func<T, bool> predicate) {
      Argument.NotNull(predicate, "predicate");

      return !predicate(value);
    }

    public static Func<T, bool> Not<T>(Func<T, bool> predicate) {
      return value => Not(value, predicate);
    }

    #endregion Not<T>(…, Func<T, bool> predicate)

    #region And<T>(…, Func<T, bool>[] predicate)

    public static bool And<T>(T value, params Func<T, bool>[] predicates) {
      return And(value, predicates ?? Enumerable.Empty<Func<T, bool>>());
    }

    public static bool And<T>(T value, IEnumerable<Func<T, bool>> predicates) {
      Argument.NotNull(predicates, "predicates");

      return !predicates.Any(predicate => !predicate(value));
    }

    public static Func<T, bool> And<T>(params Func<T, bool>[] predicates) {
      return And(predicates ?? Enumerable.Empty<Func<T, bool>>());
    }

    public static Func<T, bool> And<T>(IEnumerable<Func<T, bool>> predicates) {
      return value => And(value, predicates);
    }

    #endregion And<T>(…, Func<T, bool> predicate)

    #region Or<T>(…, Func<T, bool> predicate)

    public static bool Or<T>(T item, params Func<T, bool>[] predicates) {
      return Or(item, predicates ?? Enumerable.Empty<Func<T, bool>>());
    }

    public static bool Or<T>(T item, IEnumerable<Func<T, bool>> predicates) {
      Argument.NotNull(predicates, "predicates");

      return predicates.Any(predicate => predicate(item));
    }

    public static Func<T, bool> Or<T>(params Func<T, bool>[] predicates) {
      return Or(predicates ?? Enumerable.Empty<Func<T, bool>>());
    }

    public static Func<T, bool> Or<T>(IEnumerable<Func<T, bool>> predicates) {
      return item => Or(item, predicates);
    }

    #endregion Or<T>(…, Func<T, bool> predicate)

    #region Xor<T>(…, Func<T, bool> predicate)

    public static bool Xor<T>(T item, params Func<T, bool>[] predicates) {
      return Xor(item, predicates ?? Enumerable.Empty<Func<T, bool>>());
    }

    public static bool Xor<T>(T item, IEnumerable<Func<T, bool>> predicates) {
      Argument.NotNull(predicates, "predicates");

      return predicates.Aggregate(false, (seed, predicate) => seed ^ predicate(item));
    }

    public static Func<T, bool> Xor<T>(params Func<T, bool>[] predicates) {
      return Xor(predicates ?? Enumerable.Empty<Func<T, bool>>());
    }

    public static Func<T, bool> Xor<T>(IEnumerable<Func<T, bool>> predicates) {
      return item => Xor(item, predicates);
    }

    #endregion Xor<T>(…, Func<T, bool> predicate)

    #region All<T>(…, Action<T> action)

    public static void All<T>(T value, params Action<T>[] actions) {
      All(value, actions ?? Enumerable.Empty<Action<T>>());
    }

    public static void All<T>(T value, IEnumerable<Action<T>> actions) {
      Argument.NotNull(actions, "actions");

      foreach(var action in actions.Where(item => item != null)) {
        Contract.Assume(action != null);
        action(value);
      }//for
    }

    public static Action<T> All<T>(params Action<T>[] actions) {
      return All(actions ?? Enumerable.Empty<Action<T>>());
    }

    public static Action<T> All<T>(IEnumerable<Action<T>> actions) {
      return obj => All(obj, actions);
    }

    #endregion All<T>(…, Action<T> action)

    #region [Not]IsNull

    public static bool IsNull(object value) {
      return value == null;
    }

    public static Func<object, bool> IsNull() {
      return IsNull;
    }

    public static bool NotIsNull(object value) {
      return !IsNull(value);
    }

    public static Func<object, bool> NotIsNull() {
      return NotIsNull;
    }

    public static bool IsNull<T>(T? value) where T : struct {
      return value == null;
    }

    public static Func<T?, bool> IsNull<T>() where T : struct {
      return IsNull;
    }

    public static bool NotIsNull<T>(T? value) where T : struct {
      return !IsNull(value);
    }

    public static Func<T?, bool> NotIsNull<T>() where T : struct {
      return NotIsNull;
    }

    #endregion [Not]IsNull

    #region This

    public static T This<T>(T value) {
      return value;
    }

    #endregion This

    #region Bind's

    public static Action Bind1<TValue>(this Action<TValue> action, TValue arg1) {
      Argument.NotNull(action, "action");

      return () => action(arg1);
    }

    public static Action<TValue2> Bind1<TValue1, TValue2>(this Action<TValue1, TValue2> action, TValue1 arg1) {
      Argument.NotNull(action, "action");

      return arg2 => action(arg1, arg2);
    }

    public static Action<TValue1> Bind2<TValue1, TValue2>(this Action<TValue1, TValue2> action, TValue2 arg2) {
      Argument.NotNull(action, "action");

      return arg1 => action(arg1, arg2);
    }

    public static Func<TValue2, TResult> Bind1<TValue1, TValue2, TResult>(this Func<TValue1, TValue2, TResult> function, TValue1 arg1) {
      Argument.NotNull(function, "function");

      return arg2 => function(arg1, arg2);
    }

    public static Func<TValue1, TResult> Bind2<TValue1, TValue2, TResult>(this Func<TValue1, TValue2, TResult> function, TValue2 arg2) {
      Argument.NotNull(function, "function");

      return arg1 => function(arg1, arg2);
    }

    #endregion Bind's
  }
}
